﻿//------------------------------------------------------------------------------
// <copyright file="Hotkeys.cs" company="Robert MacLean">
//      Copyright © Robert MacLean.  All Rights Reserved.
// </copyright>
//
//  Details on this see: http://rule18.codeplex.com
//  This code released under the terms of the 
//  Microsoft Reciprocal License (MS-RL, http://opensource.org/licenses/ms-rl.html.)
//------------------------------------------------------------------------------
namespace Rule18.Code
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Forms;
    using ManagedWinapi;

    /// <summary>
    /// Handles the hotkeys for the application
    /// </summary>
    internal class Hotkeys : IDisposable
    {
        List<Hotkey> hotkeys = new List<Hotkey>();

        public event EventHandler<FlexiEventArgs<Hotkey>> KeyPress;

        /// <summary>
        /// Adds the hotkey.
        /// </summary>
        /// <param name="keyCode">The key code.</param>
        /// <param name="ctrl">if set to <c>true</c> [CTRL].</param>
        /// <param name="alt">if set to <c>true</c> [alt].</param>
        /// <param name="shift">if set to <c>true</c> [shift].</param>
        /// <param name="windowsKey">if set to <c>true</c> [windows key].</param>
        /// <exception cref="Exception">If key is already in use.</exception>
        public void AddHotkey(Keys keyCode, bool ctrl = false, bool alt = false, bool shift = false, bool windowsKey = false)
        {
            Hotkey hotkey = new Hotkey();
            hotkey.KeyCode = keyCode;
            hotkey.Ctrl = ctrl;
            hotkey.Alt = alt;
            hotkey.Shift = shift;
            hotkey.WindowsKey = windowsKey;
            hotkey.HotkeyPressed += new EventHandler<FlexiEventArgs<int>>(hotkey_HotkeyPressed);
            try
            {
                hotkey.Enabled = true;
                hotkeys.Add(hotkey);
            }
            catch (HotkeyAlreadyInUseException err)
            {
                string keyCombination = string.Empty;
                if (ctrl)
                {
                    keyCombination += "(Ctrl) + ";
                }

                if (alt)
                {
                    keyCombination += "(Alt) + ";
                }

                if (shift)
                {
                    keyCombination += "(Shift) + ";
                }

                if (windowsKey)
                {
                    keyCombination += "(Win) + ";
                }

                keyCombination += keyCode.ToString();

                throw new Exception("Hotkey combination (" + keyCombination + ") already in use", err);
            }
        }


        /// <summary>
        /// Handles the HotkeyPressed event of the hotkey control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The Rule18.Code.FlexiEventArgs&lt;int&gt; instance containing the event data.</param>
        void hotkey_HotkeyPressed(object sender, FlexiEventArgs<int> e)
        {
            Hotkey hotkey = hotkeys.Single(h => h.HotkeyIndex == e.Data);
            FlexiEventArgs<Hotkey> eventArgs = new FlexiEventArgs<Hotkey>();
            eventArgs.Data = hotkey;
            if (KeyPress != null)
            {
                KeyPress(this, eventArgs);
            }
        }

        /// <summary>
        /// Removes the hotkey.
        /// </summary>
        /// <param name="keyCode">The key code.</param>
        /// <param name="ctrl">if set to <c>true</c> [CTRL].</param>
        /// <param name="alt">if set to <c>true</c> [alt].</param>
        /// <param name="shift">if set to <c>true</c> [shift].</param>
        /// <param name="windowsKey">if set to <c>true</c> [windows key].</param>
        public void RemoveHotkey(Keys keyCode, bool ctrl = false, bool alt = false, bool shift = false, bool windowsKey = false)
        {
            Hotkey hotkey = hotkeys.First(h => h.KeyCode == keyCode && h.Ctrl == ctrl && h.Alt == alt && h.Shift == shift && h.WindowsKey == windowsKey);
            if (hotkey != null)
            {
                hotkeys.Remove(hotkey);
                hotkey.Enabled = false;
                hotkey.Dispose();
            }            
        }

        #region IDisposable Members

        /// <summary>
        /// Internal variable which checks if Dispose has already been called
        /// </summary>
        private Boolean disposed;

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        private void Dispose(Boolean disposing)
        {
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                foreach (Hotkey hotkey in hotkeys)
                {
                    hotkey.Dispose();
                }
            }

            disposed = true;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // Call the private Dispose(bool) helper and indicate 
            // that we are explicitly disposing
            this.Dispose(true);

            // Tell the garbage collector that the object doesn't require any
            // cleanup when collected since Dispose was called explicitly.
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
